package org.melanesia.beans;

import java.util.Map;

/**
 * Setter interface. Implementations of this interface should
 * <strong>know</strong> the property name and the path to access it.
 *
 * @author marcin.kielar
 */
interface Setter {

    /**
     * Sets the value of the property.
     *
     * The <code>intermediateBeanClasses</code> is the collection of classes
     * used to instantiate "intermediate" properties, in case of accessing a
     * composite property.
     *
     * This is used in situations, where we have a compound object like for
     * example:
     *
     * <pre>
     * {@code
     * class A {
     *   private B b;
     *   private String propertyC;
     *   private String propertyD;
     *   private Integer propertyE;
     * }
     *
     * interface B {
     *   String getPropertyF();
     *   void setPropertyF(String propertyF);
     * }
     * }
     * </pre>
     *
     * We can then try to access propertyF from class B using instance of class
     * A like this:
     *
     * <pre>
     * @{code
     * setter.set(bean, "b.propertyF", null);
     * }
     * </pre>
     *
     * Using this notatnion {@link CompositeSetter} will try to create an
     * instance of B and then set its propertiyF. This notatnion can go futher -
     * for example "b.c.d.propertyX".
     *
     * The problem with above is that B is an an interface. (It may also be an
     * abstract class or a superclass for some other class, that the programmer
     * wants to instantiate.)
     *
     * In the above case, we can do this:
     *
     * <pre>
     * {@code
     * class BImpl implements B {
     *   private String f;
     *   private String getPropertyF() {return f);
     *   private void setPropertyF(String propertyF) {this.f = propertyF;}
     * }
     *
     * Map<String, Class<?>> intermediateBeanClasses = new HashMap<String, Class<?>>();
     * intermediateBeanClasses.put("b", BImpl.class);
     *
     * setter.set(bean, "b.propertyF", intermediateBeanClasses);
     * }
     * </pre>
     *
     * Note, that the key to the <code>intermediateBeanClasses</code> map is a
     * <i>path</i>, not a simple property name. So in case of a multi-composite
     * property like this <code>teacher.person.address.street</code>, if
     * <code>address</code> is an abstract class, we can define its
     * intermediateBeanClass like this:
     *
     * <pre>
     * {@code
     * intermediateBeanClasses.put("teacher.person.address", AddressImpl.class);
     * }
     * </pre>
     *
     * @param bean
     *            bean to access the property
     * @param value
     *            property value to set
     * @param intermediateBeanClasses
     *            collection of classes used to instantiate "intermediate"
     *            properties in a composite property
     *
     * @see org.melanesia.sql.Query#setIntermediatePropertyClass(String, Class)
     */
    void set(Object bean, Object value, Map<String, Class<?>> intermediateBeanClasses);

    /**
     * Returns the type that is accepted by the
     * {@link #set(Object, Object, Map)} method. This may be interpreted as the
     * type of the property.
     *
     * @return setter accepted type
     */
    Class<?> getAcceptedType();
}
